package com.cencat.common.utils;

import com.cencat.common.enums.ErrorCode;
import com.cencat.common.response.ApiResponse;
import lombok.experimental.UtilityClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * API工具类
 * 提供统一的API响应格式和处理功能
 * 
 * @author cencat
 * @since 2024-01-01
 */
@UtilityClass
public class ApiUtils {

    private static final Logger log = LoggerFactory.getLogger(ApiUtils.class);

    /**
     * 成功响应
     * 
     * @param <T> 数据类型
     * @return API响应
     */
    public static <T> ApiResponse<T> success() {
        return ApiResponse.success();
    }

    /**
     * 成功响应（带数据）
     * 
     * @param data 响应数据
     * @param <T> 数据类型
     * @return API响应
     */
    public static <T> ApiResponse<T> success(T data) {
        return ApiResponse.success(data);
    }

    /**
     * 成功响应（带消息和数据）
     * 
     * @param message 响应消息
     * @param data 响应数据
     * @param <T> 数据类型
     * @return API响应
     */
    public static <T> ApiResponse<T> success(String message, T data) {
        return ApiResponse.success(message, data);
    }

    /**
     * 错误响应
     * 
     * @param errorCode 错误码
     * @param <T> 数据类型
     * @return API响应
     */
    public static <T> ApiResponse<T> error(ErrorCode errorCode) {
        return ApiResponse.error(errorCode);
    }

    /**
     * 错误响应（自定义消息）
     * 
     * @param errorCode 错误码
     * @param message 错误消息
     * @param <T> 数据类型
     * @return API响应
     */
    public static <T> ApiResponse<T> error(ErrorCode errorCode, String message) {
        return ApiResponse.error(errorCode.getCode(), message);
    }

    /**
     * 错误响应（自定义码和消息）
     * 
     * @param code 错误码
     * @param message 错误消息
     * @param <T> 数据类型
     * @return API响应
     */
    public static <T> ApiResponse<T> error(int code, String message) {
        return ApiResponse.error(code, message);
    }

    /**
     * 参数校验错误响应
     * 
     * @param bindingResult 校验结果
     * @param <T> 数据类型
     * @return API响应
     */
    public static <T> ApiResponse<T> validationError(BindingResult bindingResult) {
        if (bindingResult == null || !bindingResult.hasErrors()) {
            return error(ErrorCode.PARAM_ERROR);
        }

        List<String> errors = bindingResult.getFieldErrors()
                .stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.toList());

        String message = String.join("; ", errors);
        return error(ErrorCode.PARAM_ERROR, message);
    }

    /**
     * 参数校验错误响应（详细信息）
     * 
     * @param bindingResult 校验结果
     * @param <T> 数据类型
     * @return API响应
     */
    public static <T> ApiResponse<T> validationErrorDetail(BindingResult bindingResult) {
        if (bindingResult == null || !bindingResult.hasErrors()) {
            return error(ErrorCode.PARAM_ERROR);
        }

        Map<String, String> errors = new HashMap<>();
        bindingResult.getFieldErrors().forEach(error -> 
            errors.put(error.getField(), error.getDefaultMessage())
        );

        ApiResponse<T> response = error(ErrorCode.PARAM_ERROR, "参数校验失败");
        @SuppressWarnings("unchecked")
        T data = (T) errors;
        response.setData(data);
        return response;
    }

    // ==================== ResponseEntity 封装 ====================

    /**
     * 成功响应实体
     * 
     * @param <T> 数据类型
     * @return ResponseEntity
     */
    public static <T> ResponseEntity<ApiResponse<T>> ok() {
        return ResponseEntity.ok(success());
    }

    /**
     * 成功响应实体（带数据）
     * 
     * @param data 响应数据
     * @param <T> 数据类型
     * @return ResponseEntity
     */
    public static <T> ResponseEntity<ApiResponse<T>> ok(T data) {
        return ResponseEntity.ok(success(data));
    }

    /**
     * 成功响应实体（带消息和数据）
     * 
     * @param message 响应消息
     * @param data 响应数据
     * @param <T> 数据类型
     * @return ResponseEntity
     */
    public static <T> ResponseEntity<ApiResponse<T>> ok(String message, T data) {
        return ResponseEntity.ok(success(message, data));
    }

    /**
     * 创建成功响应实体
     * 
     * @param data 响应数据
     * @param <T> 数据类型
     * @return ResponseEntity
     */
    public static <T> ResponseEntity<ApiResponse<T>> created(T data) {
        return ResponseEntity.status(HttpStatus.CREATED).body(success(data));
    }

    /**
     * 错误响应实体
     * 
     * @param errorCode 错误码
     * @param <T> 数据类型
     * @return ResponseEntity
     */
    public static <T> ResponseEntity<ApiResponse<T>> badRequest(ErrorCode errorCode) {
        return ResponseEntity.badRequest().body(error(errorCode));
    }

    /**
     * 错误响应实体（自定义消息）
     * 
     * @param errorCode 错误码
     * @param message 错误消息
     * @param <T> 数据类型
     * @return ResponseEntity
     */
    public static <T> ResponseEntity<ApiResponse<T>> badRequest(ErrorCode errorCode, String message) {
        return ResponseEntity.badRequest().body(error(errorCode, message));
    }

    /**
     * 未授权响应实体
     * 
     * @param <T> 数据类型
     * @return ResponseEntity
     */
    public static <T> ResponseEntity<ApiResponse<T>> unauthorized() {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error(ErrorCode.UNAUTHORIZED));
    }

    /**
     * 未授权响应实体（自定义消息）
     * 
     * @param message 错误消息
     * @param <T> 数据类型
     * @return ResponseEntity
     */
    public static <T> ResponseEntity<ApiResponse<T>> unauthorized(String message) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error(ErrorCode.UNAUTHORIZED, message));
    }

    /**
     * 禁止访问响应实体
     * 
     * @param <T> 数据类型
     * @return ResponseEntity
     */
    public static <T> ResponseEntity<ApiResponse<T>> forbidden() {
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error(ErrorCode.FORBIDDEN));
    }

    /**
     * 禁止访问响应实体（自定义消息）
     * 
     * @param message 错误消息
     * @param <T> 数据类型
     * @return ResponseEntity
     */
    public static <T> ResponseEntity<ApiResponse<T>> forbidden(String message) {
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error(ErrorCode.FORBIDDEN, message));
    }

    /**
     * 资源未找到响应实体
     * 
     * @param <T> 数据类型
     * @return ResponseEntity
     */
    public static <T> ResponseEntity<ApiResponse<T>> notFound() {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error(ErrorCode.NOT_FOUND));
    }

    /**
     * 资源未找到响应实体（自定义消息）
     * 
     * @param message 错误消息
     * @param <T> 数据类型
     * @return ResponseEntity
     */
    public static <T> ResponseEntity<ApiResponse<T>> notFound(String message) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error(ErrorCode.NOT_FOUND, message));
    }

    /**
     * 服务器内部错误响应实体
     * 
     * @param <T> 数据类型
     * @return ResponseEntity
     */
    public static <T> ResponseEntity<ApiResponse<T>> internalServerError() {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error(ErrorCode.INTERNAL_SERVER_ERROR));
    }

    /**
     * 服务器内部错误响应实体（自定义消息）
     * 
     * @param message 错误消息
     * @param <T> 数据类型
     * @return ResponseEntity
     */
    public static <T> ResponseEntity<ApiResponse<T>> internalServerError(String message) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error(ErrorCode.INTERNAL_SERVER_ERROR, message));
    }

    // ==================== HTTP 响应处理 ====================

    /**
     * 写入JSON响应
     * 
     * @param response HTTP响应
     * @param apiResponse API响应对象
     */
    public static void writeJsonResponse(HttpServletResponse response, ApiResponse<?> apiResponse) {
        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(getHttpStatus(apiResponse.getCode()));
        
        try (PrintWriter writer = response.getWriter()) {
            writer.write(JsonUtils.toJson(apiResponse));
            writer.flush();
        } catch (IOException e) {
            log.error("写入JSON响应失败: {}", e.getMessage(), e);
        }
    }

    /**
     * 写入成功响应
     * 
     * @param response HTTP响应
     * @param data 响应数据
     */
    public static void writeSuccessResponse(HttpServletResponse response, Object data) {
        writeJsonResponse(response, success(data));
    }

    /**
     * 写入错误响应
     * 
     * @param response HTTP响应
     * @param errorCode 错误码
     */
    public static void writeErrorResponse(HttpServletResponse response, ErrorCode errorCode) {
        writeJsonResponse(response, error(errorCode));
    }

    /**
     * 写入错误响应（自定义消息）
     * 
     * @param response HTTP响应
     * @param errorCode 错误码
     * @param message 错误消息
     */
    public static void writeErrorResponse(HttpServletResponse response, ErrorCode errorCode, String message) {
        writeJsonResponse(response, error(errorCode, message));
    }

    // ==================== 请求信息提取 ====================

    /**
     * 获取客户端IP地址
     * 
     * @param request HTTP请求
     * @return IP地址
     */
    public static String getClientIp(HttpServletRequest request) {
        return IpUtils.getClientIp(request);
    }

    /**
     * 获取用户代理
     * 
     * @param request HTTP请求
     * @return 用户代理字符串
     */
    public static String getUserAgent(HttpServletRequest request) {
        return request.getHeader("User-Agent");
    }

    /**
     * 获取请求URL
     * 
     * @param request HTTP请求
     * @return 完整的请求URL
     */
    public static String getRequestUrl(HttpServletRequest request) {
        StringBuffer url = request.getRequestURL();
        String queryString = request.getQueryString();
        if (CencatStringUtils.isNotBlank(queryString)) {
            url.append("?").append(queryString);
        }
        return url.toString();
    }

    /**
     * 获取请求参数Map
     * 
     * @param request HTTP请求
     * @return 参数Map
     */
    public static Map<String, String> getParameterMap(HttpServletRequest request) {
        Map<String, String> params = new HashMap<>();
        Enumeration<String> paramNames = request.getParameterNames();
        while (paramNames.hasMoreElements()) {
            String paramName = paramNames.nextElement();
            String paramValue = request.getParameter(paramName);
            params.put(paramName, paramValue);
        }
        return params;
    }

    /**
     * 获取请求头Map
     * 
     * @param request HTTP请求
     * @return 请求头Map
     */
    public static Map<String, String> getHeaderMap(HttpServletRequest request) {
        Map<String, String> headers = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            headers.put(headerName, headerValue);
        }
        return headers;
    }

    /**
     * 判断是否为Ajax请求
     * 
     * @param request HTTP请求
     * @return 是否为Ajax请求
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        String requestedWith = request.getHeader("X-Requested-With");
        return "XMLHttpRequest".equals(requestedWith);
    }

    /**
     * 判断是否为JSON请求
     * 
     * @param request HTTP请求
     * @return 是否为JSON请求
     */
    public static boolean isJsonRequest(HttpServletRequest request) {
        String contentType = request.getContentType();
        return contentType != null && contentType.contains("application/json");
    }

    /**
     * 判断是否为移动端请求
     * 
     * @param request HTTP请求
     * @return 是否为移动端请求
     */
    public static boolean isMobileRequest(HttpServletRequest request) {
        String userAgent = getUserAgent(request);
        if (CencatStringUtils.isBlank(userAgent)) {
            return false;
        }
        
        userAgent = userAgent.toLowerCase();
        return userAgent.contains("mobile") || 
               userAgent.contains("android") || 
               userAgent.contains("iphone") || 
               userAgent.contains("ipad") || 
               userAgent.contains("windows phone");
    }

    // ==================== 分页处理 ====================

    /**
     * 构建分页响应
     * 
     * @param data 数据列表
     * @param total 总数
     * @param pageNum 页码
     * @param pageSize 页大小
     * @param <T> 数据类型
     * @return 分页响应
     */
    public static <T> ApiResponse<PageUtils.PageResult<T>> pageResponse(List<T> data, long total, int pageNum, int pageSize) {
        PageUtils.PageResult<T> pageResult = PageUtils.PageResult.<T>builder()
                .data(data)
                .total(total)
                .pageNum(pageNum)
                .pageSize(pageSize)
                .build();
        return success(pageResult);
    }

    /**
     * 构建分页响应
     * 
     * @param pageResult 分页结果
     * @param <T> 数据类型
     * @return 分页响应
     */
    public static <T> ApiResponse<PageUtils.PageResult<T>> pageResponse(PageUtils.PageResult<T> pageResult) {
        return success(pageResult);
    }

    // ==================== 工具方法 ====================

    /**
     * 根据业务错误码获取HTTP状态码
     * 
     * @param businessCode 业务错误码
     * @return HTTP状态码
     */
    private static int getHttpStatus(int businessCode) {
        if (businessCode == 200) {
            return HttpStatus.OK.value();
        } else if (businessCode >= 400 && businessCode < 500) {
            return businessCode;
        } else if (businessCode >= 1000) {
            // 业务错误码，统一返回200，由前端根据业务码处理
            return HttpStatus.OK.value();
        } else {
            return HttpStatus.INTERNAL_SERVER_ERROR.value();
        }
    }

    /**
     * 构建请求信息
     * 
     * @param request HTTP请求
     * @return 请求信息Map
     */
    public static Map<String, Object> buildRequestInfo(HttpServletRequest request) {
        Map<String, Object> requestInfo = new HashMap<>();
        requestInfo.put("method", request.getMethod());
        requestInfo.put("url", getRequestUrl(request));
        requestInfo.put("ip", getClientIp(request));
        requestInfo.put("userAgent", getUserAgent(request));
        requestInfo.put("parameters", getParameterMap(request));
        requestInfo.put("headers", getHeaderMap(request));
        requestInfo.put("isAjax", isAjaxRequest(request));
        requestInfo.put("isJson", isJsonRequest(request));
        requestInfo.put("isMobile", isMobileRequest(request));
        requestInfo.put("timestamp", System.currentTimeMillis());
        return requestInfo;
    }

    /**
     * 记录请求日志
     * 
     * @param request HTTP请求
     * @param response API响应
     * @param duration 处理时长（毫秒）
     */
    public static void logRequest(HttpServletRequest request, ApiResponse<?> response, long duration) {
        try {
            Map<String, Object> logInfo = new HashMap<>();
            logInfo.put("method", request.getMethod());
            logInfo.put("url", request.getRequestURI());
            logInfo.put("ip", getClientIp(request));
            logInfo.put("userAgent", getUserAgent(request));
            logInfo.put("responseCode", response.getCode());
            logInfo.put("responseMessage", response.getMessage());
            logInfo.put("duration", duration + "ms");
            
            if (response.getCode() == 200) {
                log.info("API请求成功: {}", JsonUtils.toJson(logInfo));
            } else {
                log.warn("API请求失败: {}", JsonUtils.toJson(logInfo));
            }
        } catch (Exception e) {
            log.error("记录请求日志失败: {}", e.getMessage(), e);
        }
    }

    /**
     * 构建错误详情
     * 
     * @param exception 异常
     * @return 错误详情Map
     */
    public static Map<String, Object> buildErrorDetail(Exception exception) {
        Map<String, Object> errorDetail = new HashMap<>();
        errorDetail.put("exceptionType", exception.getClass().getSimpleName());
        errorDetail.put("message", exception.getMessage());
        errorDetail.put("timestamp", System.currentTimeMillis());
        
        // 开发环境可以包含堆栈信息
        if (isDebugMode()) {
            errorDetail.put("stackTrace", getStackTrace(exception));
        }
        
        return errorDetail;
    }

    /**
     * 获取异常堆栈信息
     * 
     * @param exception 异常
     * @return 堆栈信息字符串
     */
    private static String getStackTrace(Exception exception) {
        java.io.StringWriter sw = new java.io.StringWriter();
        java.io.PrintWriter pw = new java.io.PrintWriter(sw);
        exception.printStackTrace(pw);
        return sw.toString();
    }

    /**
     * 判断是否为调试模式
     * 
     * @return 是否为调试模式
     */
    private static boolean isDebugMode() {
        // 可以通过系统属性或环境变量判断
        String profile = System.getProperty("spring.profiles.active", "prod");
        return "dev".equals(profile) || "test".equals(profile);
    }
}